package org.shoukaiseki.inittable.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import org.shoukaiseki.common.exception.MessageVirtualException;
import org.shoukaiseki.common.utils.StringUtils;
import org.shoukaiseki.inittable.config.TableBuildProperties;
import org.shoukaiseki.inittable.domain.SksInitTableVersion;
import org.shoukaiseki.inittable.domain.SksInitTableVersionQO;
import org.shoukaiseki.inittable.domain.TableColumnInfo;
import org.shoukaiseki.inittable.domain.TableInfo;
import org.shoukaiseki.inittable.exception.InitTableException;
import org.shoukaiseki.inittable.handle.TableBuildPackageScanRegistrar;
import org.shoukaiseki.inittable.handle.database.BaseInitTableHandle;
import org.shoukaiseki.inittable.handle.database.InitTableHandleMysql;
import org.shoukaiseki.inittable.logger.InitTableLoggers;
import org.shoukaiseki.inittable.mapper.SksInitTableVersionMapper;
import org.shoukaiseki.inittable.service.ITableBuildInfoService;
import org.shoukaiseki.inittable.service.ITableBuildService;
import org.shoukaiseki.kakaroto.annotation.TableBuild;
import org.slf4j.Logger;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.util.*;


@Service
public class TableBuildServiceImpl implements ITableBuildService {

    @Resource
    private SksInitTableVersionMapper sksInitTableVersionMapper;

    @Resource
    private TableBuildProperties tableBuildProperties;

    public static final Logger logger = InitTableLoggers.DEFAULT_LOGGER;

    @Resource
    private DataSource dataSource;

    @Override
    public Object initTableToVersion(boolean reset){

        Connection conn=null;

        List<BeanDefinitionHolder> beanList = TableBuildPackageScanRegistrar.getBeanList();
        Map<Class<?>, BeanDefinitionHolder> clazzMap = verification(beanList);
        List<TableInfo> tableInfoList=new ArrayList<>();
        try{
            conn = dataSource.getConnection();
            BaseInitTableHandle initTableHandleMysql=null;
            initTableHandleMysql = new InitTableHandleMysql(conn,null);
            initTableHandleMysql.setTableBuildProperties(tableBuildProperties);
            initTableHandleMysql.setSksInitTableVersionMapper(sksInitTableVersionMapper);
            initTableHandleMysql.buildSksInitTableVersion(reset);
            if(reset){
                return null;
            }
            for (Class<?> clazz : clazzMap.keySet()) {
                TableBuild tableBuild = AnnotationUtils.findAnnotation(clazz, TableBuild.class);
                TableInfo tableInfo=new TableInfo();
                tableBuildToTableInfo(clazzMap,clazz,tableBuild,tableInfo);
                initTableHandleMysql = new InitTableHandleMysql(conn,tableInfo);
                initTableHandleMysql.setSksInitTableVersionMapper(sksInitTableVersionMapper);
                initTableHandleMysql.setBeanDefinitionHolder(clazzMap.get(clazz));
                initTableHandleMysql.setClazz(clazz);
                initTableHandleMysql.insertTableInfoToVersion(tableInfo);

                initTableHandleMysql.updateCurrentMd5();


                logger.debug("{}",dataSource);

            }
        }catch (Exception e){
            throw new InitTableException("出错了",e);

        }finally {
            if (conn!=null) {
                try {
                    conn.close();
                } catch (Throwable t) {
                    logger.error("",t);
                }
            }
        }
        return null;
    }

    @Override
    public List getInitTableList(SksInitTableVersionQO sksInitTableVersionQO)  {
        BaseInitTableHandle initTableHandleMysql=null;
        initTableHandleMysql = new InitTableHandleMysql(null,null);
        initTableHandleMysql.setTableBuildProperties(tableBuildProperties);
        initTableHandleMysql.setSksInitTableVersionMapper(sksInitTableVersionMapper);
        List<SksInitTableVersion> sksInitTableVersionList = initTableHandleMysql.selectTableVersionList(sksInitTableVersionQO);
        Page<Object> list = new Page<>();
        list.setTotal(new PageInfo(sksInitTableVersionList).getTotal());
        for (SksInitTableVersion sksInitTableVersion : sksInitTableVersionList) {
            JSONObject jsonObject = (JSONObject) JSON.toJSON(sksInitTableVersion);
            String indexJson = sksInitTableVersion.getIndexJson();
            jsonObject.put("indexJson",JSONArray.parseArray(indexJson));
            String triggerJson = sksInitTableVersion.getTriggerJson();
            jsonObject.put("triggerJson",JSONArray.parseArray(triggerJson));
            list.add(jsonObject);
        }
        return list;
    }


    @Override
    public Object initTableBuild(String[] tables,int initType)  {
        List<BeanDefinitionHolder> beanList = TableBuildPackageScanRegistrar.getBeanList();

        List<String> errorIndexSqlList = new ArrayList<>();

        List<String> errorTriggerSqlList=new ArrayList<>();

        List<String> executeSqlList=new ArrayList<>();

        String[] tableNames=tables!=null?tables:new String[]{};



        Map<Class<?>, BeanDefinitionHolder> clazzMap = verification(beanList);
        Connection conn=null;
        for (Class<?> clazz : clazzMap.keySet()) {
            TableBuild tableBuild = AnnotationUtils.findAnnotation(clazz, TableBuild.class);
            String name = tableBuild.name().trim();

            TableInfo tableInfo=new TableInfo();
            if(!Arrays.asList(tableNames).contains(name)){
                continue;
            }

            BaseInitTableHandle initTableHandleMysql=null;
            try {
                conn = dataSource.getConnection();
                tableBuildToTableInfo(clazzMap,clazz,tableBuild,tableInfo);

                Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
                Object obj = declaredConstructor.newInstance();
                if(!(obj instanceof ITableBuildInfoService)){
                    throw new MessageVirtualException("未实现ITableBuildInfoService接口"+getBeanSource(clazzMap.get(clazz)));
                }
                List<TableColumnInfo> columnInfoList = ((ITableBuildInfoService) obj).tableColumnInfoList();
                if(StringUtils.isEmpty(columnInfoList)){
                    throw new MessageVirtualException("columnInfoList元素为空"+getBeanSource(clazzMap.get(clazz)));
                }

                copyTableBuildInfoToTableInfo((ITableBuildInfoService)obj,tableInfo);

                tableInfo.setColumnInfoList(columnInfoList);
                initTableHandleMysql = new InitTableHandleMysql(conn,tableInfo);
                initTableHandleMysql.setTableBuildProperties(tableBuildProperties);
                initTableHandleMysql.setSksInitTableVersionMapper(sksInitTableVersionMapper);
                initTableHandleMysql.setBeanDefinitionHolder(clazzMap.get(clazz));
                initTableHandleMysql.setClazz(clazz);
                if((initType&0b1)>0){
                    initTableHandleMysql.buildColumn();
                }
                initTableHandleMysql.buildIndex(initType);
                initTableHandleMysql.buildConstraint(initType);
                initTableHandleMysql.buildTrigger(initType);
                initTableHandleMysql.updateLastMd5();
                if (StringUtils.isNotEmpty(initTableHandleMysql.getErrorIndexSqlList())) {
                    errorIndexSqlList.addAll(initTableHandleMysql.getErrorIndexSqlList());
                }
                if (StringUtils.isNotEmpty(initTableHandleMysql.getErrorTriggerSqlList())) {
                    errorTriggerSqlList.addAll(initTableHandleMysql.getErrorTriggerSqlList());
                }


            } catch (Throwable t) {
                logger.error("",t);
                String sql="";
                if (initTableHandleMysql!=null) {
                    sql=initTableHandleMysql.getLastExecuteSql();
                }
                throw new InitTableException(getBeanSource(clazzMap.get(clazz))+" ",t).setData(sql);
            }finally {
                if (initTableHandleMysql!=null&&StringUtils.isNotEmpty(initTableHandleMysql.getExecuteSqlList())) {
                    executeSqlList.addAll(initTableHandleMysql.getExecuteSqlList());
                }
                if (conn!=null) {
                    try {
                        conn.close();
                    } catch (Throwable t) {
                        logger.error("",t);
                    }
                }
            }
            logger.debug("{}",dataSource);
        }
        return Map.of("errorIndexSqlList",errorIndexSqlList,"errorTriggerSqlList",errorTriggerSqlList,"executeSqlList",executeSqlList);

    }


    private void tableBuildToTableInfo(Map<Class<?>,BeanDefinitionHolder> clazzMap,Class<?> clazz,TableBuild tableBuild,TableInfo tableInfo) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {


        String name = tableBuild.name().trim();
        String comment = StringUtils.ifEmpty(tableBuild.comment(), tableBuild.name()).trim();
        tableInfo.setName(name);
        tableInfo.setComment(StringUtils.ifEmpty(tableBuild.comment(),tableBuild.name()).trim());
        tableInfo.setEngine(StringUtils.ifEmpty(tableBuild.engine(),tableBuildProperties.getEngine()));
        tableInfo.setDefaultCharset(StringUtils.ifEmpty(tableBuild.defaultCharset(),tableBuildProperties.getDefaultCharset()));
        tableInfo.setAutoIncrement(StringUtils.ifEmpty(tableBuild.autoIncrement(),tableBuildProperties.getAutoIncrement()));
        tableInfo.setPrimaryKey(tableBuild.primaryKey());
        tableInfo.setVersion(tableBuild.version());

        tableInfo.setBeanSource(getBeanSource(clazzMap.get(clazz)).toString());

        Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
        Object obj = declaredConstructor.newInstance();
        if(!(obj instanceof ITableBuildInfoService)){
            throw new MessageVirtualException("未实现ITableBuildInfoService接口"+getBeanSource(clazzMap.get(clazz)));
        }

        List<TableColumnInfo> columnInfoList = ((ITableBuildInfoService) obj).tableColumnInfoList();
        if(StringUtils.isEmpty(columnInfoList)){
            throw new MessageVirtualException("columnInfoList元素为空"+getBeanSource(clazzMap.get(clazz)));
        }

        copyTableBuildInfoToTableInfo((ITableBuildInfoService)obj,tableInfo);

        tableInfo.setColumnInfoList(columnInfoList);
    }

    private void copyTableBuildInfoToTableInfo(ITableBuildInfoService obj,TableInfo tableInfo){
        tableInfo.setTableIndexInfoList(((ITableBuildInfoService) obj).tableIndexInfoList());
        tableInfo.setTableConstraintInfoList(((ITableBuildInfoService)obj).tableConstraintInfoList());
        tableInfo.setTableTriggerInfoList(((ITableBuildInfoService) obj).tableTriggerList());
        tableInfo.setCreateTableInitDataList(((ITableBuildInfoService) obj).createTableInitDataList());
    }



    private Map<Class<?>,BeanDefinitionHolder> verification(List<BeanDefinitionHolder> beanList) {
        Map<String,BeanDefinitionHolder> map=new HashMap<>();;
        Map<Class<?>,BeanDefinitionHolder> clazzMap= new HashMap<>();
        try {
            for (BeanDefinitionHolder bean : beanList) {
                ScannedGenericBeanDefinition beanDefinition= (ScannedGenericBeanDefinition) bean.getBeanDefinition();
                Class<?> clazz = Class.forName(beanDefinition.getMetadata().getClassName());
                TableBuild tableBuild = AnnotationUtils.findAnnotation(clazz, TableBuild.class);

                String name = tableBuild.name();
                if (StringUtils.isEmpty(name)) {
                    throw new MessageVirtualException(getBeanSource(map.get(name))+"表名[TableBuild.name]为空");
                }
                if(map.containsKey(name)){
                    throw new MessageVirtualException("重复的表名"+getBeanSource(bean)+","+getBeanSource(map.get(name)));
                }
                map.put(name,bean);
                clazzMap.put(clazz,bean);
            }
        } catch (ClassNotFoundException e) {
            logger.error("",e);
            throw new MessageVirtualException("出错了",e);
        }
        return clazzMap;
    }

    public Object getBeanSource(BeanDefinitionHolder bean){
        return bean.getSource();
    }
}
